#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
// PARTIE MOTRICE DU LECTEUR
////////////////////////////////////////////////////////////////////////////////

LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :  ls(nomFich), ts() {}

void LecteurPhraseAvecArbre::analyse() {
        arbre=programme();
        cout << "Syntaxe correcte." << endl;
}

Noeud* LecteurPhraseAvecArbre::programme() {
// <programme> ::= debut <seq_inst> fin FIN_FICHIER

        sauterSymCour("debut");
        Noeud* si = seqInst();
        sauterSymCour("fin");
        testerSymCour("<FINDEFICHIER>");
        
        return si;
}

Noeud* LecteurPhraseAvecArbre::seqInst() {
// <seqInst> ::= <inst> ; { <inst> ; }

        NoeudSeqInst* si = new NoeudSeqInst();
        do {
                if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="lire" || ls.getSymCour()=="ecrire") 
                { 
                        si->ajouteInstruction(inst()); sauterSymCour(";"); 
                }
                else
                { 
                        si->ajouteInstruction(inst()); 
                }
        } while (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="si" || ls.getSymCour()=="tantque"
                   || ls.getSymCour()=="repeter" ||  ls.getSymCour()=="lire" || ls.getSymCour()=="ecrire"
                   || ls.getSymCour()=="pour" || ls.getSymCour()=="selon"); // tant que le symbole courant est un debut possible d'instruction...
        
        return si;
}

Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instSi> | <instTq> ...  
    
        Noeud* inst = NULL;
        if (ls.getSymCour()=="si") { inst = instSi(); }
        else if (ls.getSymCour()=="tantque") { inst = instTq(); }
        else if (ls.getSymCour()=="repeter") { inst = instRepeter(); }
        else if (ls.getSymCour()=="lire") { inst = instLire(); }
        else if (ls.getSymCour()=="ecrire") { inst = instEcrire(); }
        else if (ls.getSymCour()=="pour") { inst = instPour(); }
        else if (ls.getSymCour()=="selon") { inst = instSelon(); }
        else if (ls.getSymCour()=="<VARIABLE>") { inst = affectation(); }
        else { erreur("<instruction>"); }
        
        return inst; 
}

////////////////////////////////////////////////////////////////////////////////
// LES DIFFERENTES INSTRUCTIONS POSSIBLES
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> (= <expression> || <chaine>) || ++ || --

        testerSymCour("<VARIABLE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour()); ls.suivant();
        Noeud* exp; 

        if (ls.getSymCour()=="=")
        {
                sauterSymCour("=");
                exp = expression();
        }
        else if (ls.getSymCour()=="++")
        {
                Symbole op = ((Symbole)"+");
                Noeud* i = ts.chercheAjoute(Symbole("1")); ls.suivant();
                
                exp = new NoeudOperateurBinaire(op,var,i);
        }
        else if (ls.getSymCour()=="--")
        {
                Symbole op = ((Symbole)"-");
                Noeud* i = ts.chercheAjoute(Symbole("1")); ls.suivant();
                
                exp = new NoeudOperateurBinaire(op,var,i);
        }
        else
        {
                erreur("<affectation>");  exp = NULL;
        }
        
        return new NoeudAffectation(var,exp);
}

Noeud* LecteurPhraseAvecArbre::instSelon() {
//<instSelon> ::= selon (<variable>) { cas <entier> : <seqInst> } defaut : <seqInst> finselon
    
        sauterSymCour("selon"); sauterSymCour("("); Noeud* var = expBool(); sauterSymCour(")");
        NoeudInstSelon* si = new NoeudInstSelon; Symbole op = ((Symbole)"=="); 
   
        while (ls.getSymCour()=="cas")
        {
                sauterSymCour("cas"); 
                
                testerSymCour("<ENTIER>");
                Noeud*i = ts.chercheAjoute(ls.getSymCour()); ls.suivant();
                Noeud* exp = new NoeudOperateurBinaire(op,var,i);
                
                sauterSymCour(":"); 
                Noeud* seq = seqInst();
                si->addCase("Cas", exp, seq);
        }
       sauterSymCour("defaut"); sauterSymCour(":"); 
        Noeud* seq = seqInst();
        Noeud* exp = new NoeudOperateurBinaire(op,var,var);
        si->addCase("Defaut", exp, seq);
    
        sauterSymCour("finselon");
    
        return si;
}

Noeud* LecteurPhraseAvecArbre::instSi() {
// <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi    
    
        NoeudInstSi* si = new NoeudInstSi;
        sauterSymCour("si"); sauterSymCour("("); Noeud* exp = expBool(); sauterSymCour(")");
        Noeud* seq = seqInst();
        si->addCondition("Si", exp,seq); 
      
        while (ls.getSymCour()=="sinonsi")
        {
                sauterSymCour("sinonsi"); sauterSymCour("("); exp = expBool(); sauterSymCour(")");
                seq = seqInst();
                si->addCondition("Sinonsi", exp,seq);
        }
        if (ls.getSymCour()=="sinon")
        {
                sauterSymCour("sinon");
                seq = seqInst();
                Noeud* exp = ts.chercheAjoute((Symbole)"1");
                si->addCondition("Sinon", exp,seq);
        }
        sauterSymCour("finsi");
    
        return si;
}

Noeud* LecteurPhraseAvecArbre::instTq() {
// <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
    
        sauterSymCour("tantque"); sauterSymCour("("); Noeud* exp = expBool(); sauterSymCour(")");
        Noeud* seq = seqInst();
        sauterSymCour("fintantque");
        
        return new NoeudInstTq(exp, seq);
}

Noeud* LecteurPhraseAvecArbre::instPour() {
// <instPour> ::= pour (<affectation> <expBool> <affectation>) <seqInst> finpour
    
        sauterSymCour("pour"); sauterSymCour("("); 
        
        Noeud* a1 = affectation(); sauterSymCour(";");  
        Noeud* exp = expBool();  sauterSymCour(";");
        Noeud* a2 = affectation();
        
        sauterSymCour(")");
        Noeud* seq = seqInst(); 
        sauterSymCour("finpour");
        
        return new NoeudInstPour(a1,exp, a2, seq);
}

Noeud* LecteurPhraseAvecArbre::instRepeter() {
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
    
        sauterSymCour("repeter");
        Noeud* seq = seqInst();
        sauterSymCour("jusqua"); sauterSymCour("("); Noeud* exp = expBool(); sauterSymCour(")");
        
        return new NoeudInstRepeter(exp, seq);
}

Noeud* LecteurPhraseAvecArbre::instLire() {
// <instLire> ::= lire ( <variable> )
    
        sauterSymCour("lire"); sauterSymCour("(");
        testerSymCour("<VARIABLE>");  Noeud* var = ts.chercheAjoute(ls.getSymCour()); ls.suivant(); 
        sauterSymCour(")");
                
        return new NoeudInstLire(var);
}

Noeud* LecteurPhraseAvecArbre::instEcrire() {
// <instEcrire> ::= ecrire ( <expression> | <chaine> )

        sauterSymCour("ecrire"); sauterSymCour("("); Noeud* it;
        
        if (ls.getSymCour()=="<CHAINE>") { it = chaine(); }
        else { it = expression(); }
        sauterSymCour(")");
        
        return new NoeudInstEcrire(it);
}

////////////////////////////////////////////////////////////////////////////////
// OUTILS
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <facteur> { <opBinaire> <facteur> }

        Noeud* fact = facteur();
        
        while (ls.getSymCour()=="+" || ls.getSymCour()=="-")
        {
                Symbole operateur = opAdd();                                                     // on stocke le symbole de l'opérateur
                Noeud* factDroit=facteur();                                                        // lecture de l'operande droit
                fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
        }
        
        return fact;
}

Noeud* LecteurPhraseAvecArbre::expBool() {
// <expBool> ::= <relation> { <opBool> <relation> }

        Noeud* rel = relation();

        while (ls.getSymCour()=="et" || ls.getSymCour()=="ou")
        {
                Symbole op = opBool();
                Noeud* relDroit = relation();
                rel = new NoeudOperateurBinaire(op, rel, relDroit);
        }
                
        return rel;
}

Noeud* LecteurPhraseAvecArbre::chaine() {
//<chaine> ::= <chaine>
        
        testerSymCour("<CHAINE>");
        Noeud* var = ts.chercheAjoute(ls.getSymCour()); ls.suivant();

        return var;
}

Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> )

        Noeud* fact=NULL;

        if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>") 
        {
                fact = ts.chercheAjoute(ls.getSymCour()); ls.suivant();
        }
        else if (ls.getSymCour()=="-") 
        {
                ls.suivant();	
                fact = new NoeudOperateurBinaire(Symbole("non"), ts.chercheAjoute(Symbole("0")), facteur()); // on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
        }
        else if (ls.getSymCour()=="non") 
        {
                ls.suivant();	
                fact = new NoeudOperateurBinaire(Symbole("non"), ts.chercheAjoute(Symbole("0")), facteur());
        }
        else if (ls.getSymCour()=="(") 
        {
                ls.suivant();
                fact = expBool();
                sauterSymCour(")");
        } 
        else erreur("<facteur>");

        return fact;
}

Noeud* LecteurPhraseAvecArbre::relation() {
 // <relation> ::= <expression> { <opRel> <expression> }

        Noeud* exp = expression();
 
        while (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<=" 
                || ls.getSymCour()==">=" || ls.getSymCour()=="<" || ls.getSymCour()==">")
        {
                Symbole op = opRel();
                Noeud* expDroit = expression();
                exp = new NoeudOperateurBinaire(op, exp, expDroit);
        }
 
        return exp;
}

Noeud* LecteurPhraseAvecArbre::terme() {
//<terme> ::= <facteur> { <opMult> <facteur> }

        Noeud* fact = facteur();
        
        while (ls.getSymCour() == "*" || ls.getSymCour() == "/") 
        {
                Symbole operateur = opMult();
                Noeud* factDroit = facteur();
                fact = new NoeudOperateurBinaire(operateur, fact, factDroit); 
        }
        
        return fact;
}

////////////////////////////////////////////////////////////////////////////////
// OPERATEURS
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult() {
// <opMult> ::= *  | /

        Symbole operateur;
        
        if (ls.getSymCour()=="*" || ls.getSymCour()=="/") { operateur=ls.getSymCour(); ls.suivant(); }
        else erreur("<opMult>");
        
        return operateur;
}

Symbole LecteurPhraseAvecArbre::opAdd() {
// <opAdd> ::= + | - 

        Symbole operateur;
        
        if (ls.getSymCour()=="+" || ls.getSymCour()=="-") { operateur=ls.getSymCour(); ls.suivant(); }
        else erreur("<opAdd>");
        
        return operateur;
}

Symbole LecteurPhraseAvecArbre::opBool() {
// <opBool> ::= et | ou 

        Symbole operateur;
        if (ls.getSymCour()=="et" || ls.getSymCour()=="ou")  { operateur=ls.getSymCour(); ls.suivant(); }
        else erreur("<opBool>");
        
        return operateur;
}
 
Symbole LecteurPhraseAvecArbre::opRel() { 
 // <opRel> ::= == | != | < | <= | > | >=

        Symbole operateur;
        
        if (ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<=" 
        || ls.getSymCour()==">=" || ls.getSymCour()=="<" || ls.getSymCour()==">")
        { 
                operateur=ls.getSymCour(); ls.suivant(); 
        }
        else erreur("<opRel>");
        
        return operateur;
 }
 
Symbole LecteurPhraseAvecArbre::opUnaire()  {
 // <opUnaire> ::= - | non

        Symbole operateur;
        
        if (ls.getSymCour()=="-" || ls.getSymCour()=="non") { operateur=ls.getSymCour(); ls.suivant(); }
        else erreur("<opUnaire>");
        
        return operateur;
 }

////////////////////////////////////////////////////////////////////////////////
// HELPERS
////////////////////////////////////////////////////////////////////////////////

void LecteurPhraseAvecArbre::testerSymCour(string ch) {
    
        if (ls.getSymCour() != ch)
        {
                cout << endl << "-------- Erreur ligne " << ls.getLigne()
                                      << " - Colonne " << ls.getColonne() << endl 
                                      << "   Attendu : " << ch << endl
                                      << "   Trouve  : " << ls.getSymCour() << endl
                                      << endl;
                exit(0); // plus tard, on levera une exception
        }
}

void LecteurPhraseAvecArbre::sauterSymCour(string ch) { testerSymCour(ch); ls.suivant(); }

void LecteurPhraseAvecArbre::erreur(string mess) {

cout << endl << "-------- Erreur ligne " << ls.getLigne() 
                      << " - Colonne " << ls.getColonne() << endl 
                       << "   Attendu : " << mess << endl
                       << "   Trouve  : " << ls.getSymCour() << endl 
                       << endl;
exit(0); // plus tard, on levera une exception

}

////////////////////////////////////////////////////////////////////////////////